home *** CD-ROM | disk | FTP | other *** search
/ The 640 MEG Shareware Studio 2 / The 640 Meg Shareware Studio CD-ROM Volume II (Data Express)(1993).ISO / clang / bccapp.zip / ACCESS.C next >
C/C++ Source or Header  |  1991-09-15  |  9KB  |  416 lines

  1. /*
  2.  *
  3.  * Structured file I/O.  Provides structure I/O for both DATABASE and
  4.  * INDEX classes.  In addition, we will provide the means to add/delete
  5.  * records as necessary
  6.  *
  7.  * (C) 1990 Vision Software
  8.  *
  9.  * $Id: access.c 1.2003 91/05/08 14:02:17 pcalvin beta $
  10.  *
  11.  * Comments:
  12.  *
  13.  * This class provides lowlevel (ANSI-C) file I/O for the DATABASE and
  14.  * index classes.  By centralizing this class, we may provide extended
  15.  * service (such as caching) without breaking the other code.  In addition
  16.  * We provide services to maintain/add/delete records..
  17.  *
  18.  * Bugs:
  19.  *
  20.  *    None documented
  21.  *
  22.  */
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <io.h>
  27.  
  28. #include <stdhdr.h>
  29.  
  30. #include <access.h>
  31. #include <adl.h>
  32.  
  33. /*
  34.  * Opens stream based upon szFileName
  35.  */
  36. ACCESS::ACCESS(PINF pinf,CCH cchRecord,SZ szFileName,SZ szExtension,BOOL fCreateIfNeeded)
  37.     {
  38.     Assert(pinf != pinfNil);
  39.     Assert(szFileName != szNil);
  40.     Assert(cchRecord != cchNil);
  41.  
  42.     SZ sz = SzFullPathFromSzSz(szFileName,szExtension);
  43.  
  44.     pinfBase = pinf;
  45.     cch = cchRecord;
  46.     rgchStorage = new char[cch];
  47.     stmFile = fopen(sz,"rb+");
  48.  
  49.     /*
  50.      * Will only create the file if the user wants it..
  51.      */
  52.     if (stmFile == stmNil)
  53.         {
  54.         if (fCreateIfNeeded)
  55.             {
  56.             Verify(FCreateFile(sz));
  57.             }
  58.         else
  59.             {
  60.             IOError("Unable to Open %s",sz);
  61.             }
  62.         }
  63.     else
  64.         {
  65.         /*
  66.          * Determine the number of records already in the file.
  67.          */
  68.         recMax = RecFromStmCch(stmFile,cch);
  69.         recCurrent = recError;
  70.         
  71.         /*
  72.          * Read in the header.  To avoid disk-seeks, we store the header
  73.          * in a variable.
  74.          */
  75.         rewind(stmFile);
  76.         Verify(fread(&fhd,sizeof(fhd),1,stmFile) == 1);
  77.         }
  78.  
  79.     
  80.     /*
  81.      *    Check the file header.  If sizes are different, ask about
  82.      *    an update procedure.
  83.      */
  84.      if (fhd.cchSizeofRecord != cch)
  85.          {
  86.          if (FAskSz("Record size has changed, update file?","Press \"Y\" to fixup datafile, \"N\" to ignore"))
  87.              {
  88.              fclose(stmFile);
  89.  
  90.              Verify(FUpdateDatabase(pinfBase,sz,cch,fhd.cchSizeofRecord));
  91.              }
  92.         }
  93.     }
  94.  
  95. /*
  96.  * Destruction is simple, standard fclose()
  97.  */
  98. ACCESS::~ACCESS()
  99.     {
  100.     delete rgchStorage;
  101.     fclose(stmFile);
  102.     }
  103.  
  104. /*
  105.  *    Marks the current record
  106.  */
  107. BOOL ACCESS::FMark()
  108.     {
  109.     recMarked = RecQuery();
  110.     return (fTrue);
  111.     }
  112.  
  113. /*
  114.  *    Goes to the previously marked record
  115.  */
  116. BOOL ACCESS::FGotoMark()
  117.     {
  118.     return (FGotoRec(recMarked));
  119.     }
  120.     
  121. /*
  122.  * Answers with the base of the record
  123.  */
  124. PINF ACCESS::PinfQuery(VOID)
  125.     {
  126.     return (pinfBase);
  127.     }
  128.  
  129. /*
  130.  * Saves the current record.  Does not adjust the record pointer
  131.  */
  132. BOOL ACCESS::FSave(VOID)
  133.     {
  134.     if (fseek(stmFile,sizeof(FHEADER)+recCurrent*cch,SEEK_SET) == 0)
  135.         {
  136.         Verify(fwrite(pinfBase,cch,1,stmFile) == 1);
  137.         return (fTrue);
  138.         }
  139.     else
  140.         {
  141.         return (fFalse);
  142.         }
  143.     }
  144.  
  145. /*
  146.  * Answers if the current record has been deleted..
  147.  */
  148. BOOL ACCESS::FDelete(VOID)
  149.     {
  150.     REC recDelete = RecQuery();
  151.     
  152.     /*
  153.      *    If none have been deleted, make this the first..
  154.      */
  155.     if (fhd.recFirstDeleted == recError)
  156.         fhd.recFirstDeleted = recDelete;
  157.  
  158.     /*
  159.      *    Clear all info from the record..
  160.      */
  161.     memset(pinfBase,0,cch);
  162.     pinfBase->recNextDeleted = recError;
  163.     
  164.     Verify(FSave());
  165.     
  166.     /*
  167.      *    Keep link to last in deleted chain..
  168.      */
  169.     if (FGotoRec(fhd.recLastDeleted))
  170.         {
  171.         pinfBase->recNextDeleted = recDelete;
  172.  
  173.         Verify(FSave());
  174.         }
  175.  
  176.     /*
  177.      *    Update pointer to new end and save.
  178.      */
  179.     fhd.recLastDeleted = recDelete;
  180.     rewind(stmFile);
  181.     Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
  182.  
  183.     return (fTrue);
  184.     }
  185.  
  186. /*
  187.  * Goes to the first record in the list.  Due to the ability to delete
  188.  * records, this need not be the first physical record..
  189.  *
  190.  * NOTE: Does not MODIFY to record in pinfBase if the record is not found
  191.  */
  192. BOOL ACCESS::FFirst(VOID)
  193.     {
  194.     return (FGotoRec(fhd.recFirstActive));
  195.     }
  196.  
  197. /*
  198.  * Makes the desired record the first logical record.  This happens (internally)
  199.  * if the first record is deleted.  It could also be called (externally) if
  200.  * an INDEX root is deleted/created
  201.  */
  202. BOOL ACCESS::FMakeFirstRec(REC rec)
  203.     {
  204.     fhd.recFirstActive = rec;
  205.     rewind(stmFile);
  206.     Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
  207.  
  208.     return (fTrue);
  209.     }
  210.  
  211. /*
  212.  * Answer if the specific physical record can be found
  213.  */
  214. BOOL ACCESS::FGotoRec(REC recDest)
  215.     {
  216.     if (recDest >= recMax || recDest == recError)
  217.         {
  218.         return (fFalse);
  219.         }
  220.     else if ((fseek(stmFile,sizeof(FHEADER)+recDest*cch,SEEK_SET) == 0) && (fread(pinfBase,cch,1,stmFile) == 1))
  221.         {
  222.         recCurrent = recDest;
  223.         return (fTrue);
  224.         }
  225.     else
  226.         {
  227.         return (fFalse);
  228.         }
  229.     }
  230.  
  231. /*
  232.  * Answers if a new record has been created..
  233.  */
  234. REC ACCESS::RecCreate()
  235.     {
  236.     /*
  237.      *    Try to create a new record from the scraps left over
  238.      *    by previous deletes.
  239.      *    Before search, save contents of created record.
  240.      */
  241.     if (fhd.recFirstDeleted != recError)
  242.         {
  243.         memcpy(rgchStorage,pinfBase,cch);
  244.         
  245.         Verify(FGotoRec(fhd.recFirstDeleted));
  246.         fhd.recFirstDeleted = pinfBase->recNextDeleted;
  247.         rewind(stmFile);
  248.         Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
  249.         
  250.         memcpy(pinfBase,rgchStorage,cch);
  251.         }
  252.     else
  253.         {
  254.         recCurrent = recMax;
  255.         recMax += 1;
  256.         Verify(FSave());
  257.         }
  258.  
  259.     /*
  260.      *    Assert that garbage does not get left here..
  261.      */
  262.     pinfBase->recNextDeleted = recError;
  263.     
  264.     /*
  265.      * If there are no other active records, I guess this is the first..
  266.      */
  267.     if (fhd.recFirstActive == recError)
  268.         {
  269.         Verify(FMakeFirstRec(recCurrent));
  270.         }
  271.  
  272.     return (recCurrent);
  273.     }
  274.  
  275. /*
  276.  * Answers with the current physical record number
  277.  */
  278. REC ACCESS::RecQuery(VOID)
  279.     {
  280.     return (recCurrent);
  281.     }
  282.  
  283. /*
  284.  * Answers with the absolute number of records in the file.
  285.  * NOTE: Includes deleted records
  286.  */
  287. REC ACCESS::RecMaxQuery(VOID)
  288.     {
  289.     return (recMax);
  290.     }
  291.  
  292. /*
  293.  * Answers with the number of records in a stream.
  294.  *
  295.  * NOTE: This is a DOS KLUDGE.  Porting to UN*X or other civilized??
  296.  * operating system will require modification here
  297.  */
  298. REC ACCESS::RecFromStmCch(FILE *stmInput,CCH cchInput)
  299.     {
  300.     LONG cbytes = filelength(fileno(stmInput));
  301.  
  302.     return ((INT)(cbytes / cchInput));
  303.     }
  304.  
  305. /*
  306.  * Using the static member szDataPath, we answer with a string that
  307.  * contains the entire pathname (including extension) for the
  308.  * desired file.
  309.  *
  310.  * NOTE: This is a TEMPORARY string.  Successive calls to this function
  311.  *         provide the same address, with a different string.
  312.  */
  313. SZ ACCESS::SzFullPathFromSzSz(SZ sz,SZ szExtension)
  314.     {
  315.     PointerAssert(sz);
  316.     PointerAssert(szExtension);
  317.  
  318.     STATIC SZTEMP szAnswer;
  319.  
  320.     if (szDataPath == szNil)
  321.         {
  322.         strcpy(szAnswer,sz);
  323.         strcat(szAnswer,szExtension);
  324.         }
  325.     else
  326.         {
  327.         strcpy(szAnswer,szDataPath);
  328.         strcat(szAnswer,sz);
  329.         strcat(szAnswer,szExtension);
  330.         }
  331.  
  332.     return (szAnswer);
  333.     }
  334.  
  335.  
  336. /*
  337.  *    Creates the file and initializes the header
  338.  */
  339. BOOL ACCESS::FCreateFile(SZ sz)
  340.     {
  341.     stmFile = fopen(sz,"wb+");
  342.     
  343.     if (stmFile == stmNil)
  344.         {
  345.         IOError("Unable to Create %s",sz);
  346.         NotReached();
  347.         }
  348.     else
  349.         {
  350.         fhd.cchSizeofRecord = cch;
  351.         fhd.recFirstDeleted = recError;
  352.         fhd.recLastDeleted = recError;
  353.         fhd.recFirstActive = recError;
  354.  
  355.         Verify(fwrite(&fhd,sizeof(fhd),1,stmFile) == 1);
  356.  
  357.         /*
  358.          * Assert that no records are available..
  359.          */
  360.         recMax = recNil;
  361.         recCurrent = recError;
  362.         }
  363.  
  364.     return (fTrue);
  365.     }
  366.     
  367. /*
  368.  *    Updates the database because the number of bytes within
  369.  *    each record has been perverted.  If the value
  370.  *    has gone up, the user may place a "default" value.  If
  371.  *    the database has gone down, the record is truncated.
  372.  */
  373. BOOL ACCESS::FUpdateDatabase(PINF pinf,SZ sz,CCH cchNew,CCH cchOld)
  374.     {
  375.     SZ szWork = tmpnam(NULL);
  376.     SZTEMP szTmp;
  377.     SZTEMP szOriginal;
  378.  
  379.     /* 
  380.      *    Be sure the TMP file gets placed in the correct directory
  381.      */
  382.     Verify(strcpy(szOriginal,sz) != szNil); 
  383.     Verify(strcpy(szTmp,SzFullPathFromSzSz(szWork,"")) != szNil);
  384.  
  385.     /* 
  386.      *    Assert the file may be renamed..
  387.      */
  388.     if (rename(szOriginal,szTmp) == 0)
  389.         {    
  390.         ACCESS acc(pinf,cchOld,szWork,"",fFalse);
  391.         REC rec = recNil;
  392.  
  393.         /* 
  394.          *    Creates the NEW database file that is going to be fixedup
  395.          */
  396.         Verify(FCreateFile(szOriginal)); 
  397.  
  398.         /*
  399.          *    Now, traverse the old database and save the contents 
  400.          *    in the new one..
  401.          */
  402.         while (acc.FGotoRec(rec))
  403.             {
  404.             Verify(RecCreate() == rec);
  405.             Verify(FSave());
  406.  
  407.             rec++;
  408.             }
  409.         }
  410.  
  411.     /*
  412.      *    And finally, delete the old one..
  413.      */
  414.     return (!remove(szTmp)); 
  415.     }
  416.